package com.sunlands.qdcy.tongzhuo.service;

import com.google.common.collect.Lists;
import com.sunlands.qdcy.tongzhuo.constant.CommonResultMessage;
import com.sunlands.qdcy.tongzhuo.constant.RedisKeyConst;
import com.sunlands.qdcy.tongzhuo.constant.YN;
import com.sunlands.qdcy.tongzhuo.exception.CheckParamException;
import com.sunlands.qdcy.tongzhuo.exception.CheckRunException;
import com.sunlands.qdcy.tongzhuo.feign.vo.UserInfoDTO;
import com.sunlands.qdcy.tongzhuo.model.DiaryDO;
import com.sunlands.qdcy.tongzhuo.model.UserDiaryReplyDO;
import com.sunlands.qdcy.tongzhuo.model.UserDiaryZanDO;
import com.sunlands.qdcy.tongzhuo.repository.UserDiaryReplyRepository;
import com.sunlands.qdcy.tongzhuo.util.RedisDistributedLockImpl;
import com.sunlands.qdcy.tongzhuo.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Caching;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import static com.sunlands.qdcy.tongzhuo.config.RedisConfig.CacheKeys.DIARY_DETAIL_BY_ID;

/**
 * @author gaoyanfeng
 * @title: UserDiaryService
 * @description: 回复
 * @date 2019/5/2914:35
 */
@Service
@Slf4j
public class UserDiaryReplyService extends BaseService{

    @Autowired
    private UserDiaryReplyRepository userDiaryReplyRepository;
    @Autowired
    private UserDiaryZanService userDiaryZanService;
    @Lazy
    @Autowired
    private DiaryService userDiaryService;
    @Lazy
    @Autowired
    private ForwardService forwardService;

    @Autowired
    private DiaryMessageService diaryMessageService;
    @Autowired
    private RedisDistributedLockImpl redisDistributedLock;

    private void deleteDiaryCacheKey(Long diaryId){
        String userDiaryCommentCount = RedisKeyConst.getUserDiaryCommentCount(diaryId);
        redisTemplate.delete(userDiaryCommentCount);
    }

    private void deleteReplyCacheKey(Long commentId, Long diaryId){
        String userCommentCount = RedisKeyConst.getUserCommentReplyCount(commentId);
        redisTemplate.delete(userCommentCount);
        deleteDiaryCacheKey(diaryId);
    }

    public Integer getCommentCountByDiaryId(Long diaryId){
        String userDiaryCommentCountKey = RedisKeyConst.getUserDiaryCommentCount(diaryId);
        Object o = redisTemplate.opsForValue().get(userDiaryCommentCountKey);
        if(o != null){
            return (Integer) o;
        }
        Long count = userDiaryReplyRepository.countByDiaryId(diaryId);
        redisTemplate.opsForValue().set(userDiaryCommentCountKey,count.intValue(), 300, TimeUnit.SECONDS);
        return count.intValue();
    }

    public Integer getReplyCountByCommentId(Long commentId){
        String userDiaryCommentCountKey = RedisKeyConst.getUserCommentReplyCount(commentId);
        Object o = redisTemplate.opsForValue().get(userDiaryCommentCountKey);
        if(o != null){
            return (Integer)o;
        }
        Long count = userDiaryReplyRepository.countByCommentId(commentId) - 1;
        redisTemplate.opsForValue().set(userDiaryCommentCountKey,count, 300, TimeUnit.SECONDS);
        return count.intValue();
    }

    /**
     * 单个评论详情
     * @param id
     * @param page
     * @param size
     * @return
     */
    @Async("diaryTaskExecutor")
    public Future<UserDiaryCommentVO> pageByComentId(Long id, Long userId, Integer page, Integer size) {
        UserDiaryReplyDO userDiaryReply = getByReplyId(id);
        UserDiaryCommentVO userDiaryComment = UserDiaryCommentVO.from(userDiaryReply);
        userDiaryComment.setZanCommentCount(userDiaryZanService.getZanCountByDiaryId(id, UserDiaryZanDO.TypeEnums.REPLY.code));
        UserInfoDTO commentUser = getUserById(userDiaryReply.getReplyUserId());
        userDiaryComment.setIdentityName(commentUser.getIdentityName());
        userDiaryComment.setUserAvatarUrl(commentUser.getAvatarUrl());
        userDiaryComment.setUserName(commentUser.getName());
        userDiaryComment.setUserCurrentLearnTargetName(commentUser.getCurrentLearnTargetName());
        if (null != userId) {
            userDiaryComment.setZan(userDiaryZanService.getByTargetIdAndTypeAndUserId(id, UserDiaryZanDO.TypeEnums.REPLY.code, userId) == null ? false:true);
        }
        Specification<UserDiaryReplyDO> specification = new Specification<UserDiaryReplyDO>() {
            @Override
            public Predicate toPredicate(Root<UserDiaryReplyDO> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicateList = Lists.newArrayList();
                predicateList.add(criteriaBuilder.equal(root.get("commentId").as(Long.class), id));
                predicateList.add(criteriaBuilder.equal(root.get("status").as(Long.class), YN.YES.getCode()));
                predicateList.add(criteriaBuilder.in(root.get("type").as(Integer.class)).value(UserDiaryReplyDO.TypeEnums.REPLY_COMMENT.code).value(UserDiaryReplyDO.TypeEnums.REPLY_REPLY.code));
                return criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
            }
        };
        Sort sort = new Sort(Sort.Direction.DESC, Lists.newArrayList("createTime"));
        PageRequest pageRequest = new PageRequest(page, size, sort).previous();
        org.springframework.data.domain.Page pageResult = userDiaryReplyRepository.findAll(specification, pageRequest);
        List<UserDiaryReplyDO> content = pageResult.getContent();
        if (null == content || content.size() == 0) {
            return new AsyncResult<>(userDiaryComment);
        }
        userDiaryComment.setCommentReplyCount(getReplyCountByCommentId(id));
        List<UserDiaryCommentVO.InnerDiaryReply> innerReplyList = Lists.newArrayList();
        for (UserDiaryReplyDO diaryReply : content) {
            UserDiaryCommentVO.InnerDiaryReply innerDiaryReply = UserDiaryCommentVO.InnerDiaryReply.from(diaryReply);
            innerReplyList.add(innerDiaryReply);
            innerDiaryReply.setZanCount(userDiaryZanService.getZanCountByDiaryId(diaryReply.getId(), UserDiaryZanDO.TypeEnums.REPLY.code));
            UserInfoDTO replyUser = getUserById(diaryReply.getReplyUserId());
            innerDiaryReply.setReplyUserName(replyUser.getName());
            innerDiaryReply.setReplyUserAvatarUrl(replyUser.getAvatarUrl());
            innerDiaryReply.setUserCurrentLearnTargetName(replyUser.getCurrentLearnTargetName());
            innerDiaryReply.setReplyIdentity(replyUser.getIdentityName());
            UserInfoDTO replyedUser = getUserById(diaryReply.getReplyedUserId());
            innerDiaryReply.setReplyedUserName(replyedUser.getName());
            innerDiaryReply.setType(diaryReply.getType());
            innerDiaryReply.setAuthor(userDiaryReply.getReplyedUserId() == innerDiaryReply.getReplyUserId().longValue() ? true : false);
            if (null != userId) {
                innerDiaryReply.setZan(userDiaryZanService.getByTargetIdAndTypeAndUserId(diaryReply.getId(), UserDiaryZanDO.TypeEnums.REPLY.code, userId) == null ? false:true);
            }
            innerDiaryReply.setUserName(replyUser.getName());
            innerDiaryReply.setIdentityName(replyUser.getIdentityName());
            innerDiaryReply.setUserAvatarUrl(replyedUser.getAvatarUrl());
            innerDiaryReply.setZanCommentCount(innerDiaryReply.getZanCount());
        }
        Page<UserDiaryCommentVO.InnerDiaryReply> pageResultVO = new Page<>();
        pageResultVO.setRows(innerReplyList);
        pageResultVO.setTotal(pageResult.getTotalElements());
        pageResultVO.setPageNumber(page);
        userDiaryComment.setInnerDiaryReplyListPage(pageResultVO);
        return new AsyncResult<>(userDiaryComment);
    }

    public UserDiaryReplyDO getByReplyId(Long replyId) {
        String key = RedisKeyConst.getUserDiaryReply(replyId);
        Object o = redisTemplate.opsForValue().get(key);
        if (null != o) {
            return (UserDiaryReplyDO) o;
        }
        UserDiaryReplyDO userDiaryReply = userDiaryReplyRepository.findOne(replyId);
        if (null == userDiaryReply) {
            return null;
        }
        redisTemplate.opsForValue().set(key, userDiaryReply, 300, TimeUnit.SECONDS);
        return userDiaryReply;
    }

    public void deleteReplyById(Long id) {
        UserDiaryReplyDO userDiaryReply = getByReplyId(id);
        if (null == userDiaryReply) {
            throw new CheckParamException("回复不存在"+id);
        }
        redisTemplate.delete(RedisKeyConst.getUserDiaryReply(id));
        deleteReplyCacheKey(userDiaryReply.getCommentId(), userDiaryReply.getDiaryId());
        userDiaryReplyRepository.updateStatusById(YN.NO.getCode(), id);
    }

    @Caching(evict = {
            @CacheEvict(cacheNames = DIARY_DETAIL_BY_ID, key = "#userDiaryReply.diaryId", condition = "#userDiaryReply.diaryId != null")
    })
    public UserDiaryListVO addReply(UserDiaryReplyDO userDiaryReply) {
        try {
            redisDistributedLock.lock(RedisKeyConst.getReplyLock(userDiaryReply.getReplyUserId()), 2000, 5000,500, 5);
            UserDiaryReplyDO save = userDiaryReplyRepository.save(userDiaryReply);
            UserDiaryReplyDO save1 = save(save);
            if (null != save1) {
                save = save1;
            }

            UserDiaryReplyDO.TypeEnums typeEnums = UserDiaryReplyDO.TypeEnums.create(userDiaryReply.getType());
            try {
                if (UserDiaryReplyDO.TypeEnums.DIARY_COMMENT == typeEnums) {
                    diaryMessageService.interactReplyDiary(userDiaryReply.getReplyUserId().longValue(), userDiaryReply.getReplyedUserId().longValue());
                } else if (UserDiaryReplyDO.TypeEnums.REPLY_COMMENT == typeEnums || UserDiaryReplyDO.TypeEnums.REPLY_REPLY == typeEnums) {
                    diaryMessageService.interactReplyComment(userDiaryReply.getReplyUserId().longValue(), userDiaryReply.getReplyedUserId().longValue());
                }
            } catch (Exception e) {
                log.error("回复调用消息系统异常 e={}",e.getStackTrace());
                sendExceptionUtil.sendException("回复调用消息系统异常"+e.getMessage(), e);
            }

            UserDiaryListVO from = UserDiaryListVO.from(save);
            UserInfoDTO userById = getUserById(save.getReplyUserId());
            from.setReplyUserAvatarUrl(userById.getAvatarUrl());
            from.setReplyUserName(userById.getName());
            from.setUserCurrentLearnTargetName(userById.getCurrentLearnTargetName());
            DiaryDO diary = userDiaryService.findById(save.getDiaryId());
            from.setAuthor(diary.getUserId() == save.getReplyUserId().longValue());
            UserInfoDTO userById1 = getUserById(save.getReplyedUserId());
            from.setReplyedUserName(userById1.getName());
            from.setZan(false);
            from.setZanCount(0);
            from.setZanCommentCount(0);
            from.setCreateTime(new Date());
            from.setUserName(userById.getName());
            from.setUserAvatarUrl(userById.getAvatarUrl());
            from.setIdentityName(userById.getIdentityName());
            return from;
        } finally {
            redisDistributedLock.release(RedisKeyConst.getReplyLock(userDiaryReply.getReplyUserId()));
        }
    }

    private UserDiaryReplyDO save(UserDiaryReplyDO userDiaryReply) {
        UserDiaryReplyDO save = null;
        if (UserDiaryReplyDO.TypeEnums.DIARY_COMMENT == UserDiaryReplyDO.TypeEnums.create(userDiaryReply.getType())) {
            userDiaryReply.setCommentId(userDiaryReply.getId());
            save = userDiaryReplyRepository.save(userDiaryReply);
        }
        deleteReplyCacheKey(userDiaryReply.getCommentId(), userDiaryReply.getDiaryId());
        return save;
    }

    // TODO 转发评论到群
    public ForwardCommentVO forwardCommentMyDiary(Long commentId) {
        UserDiaryReplyDO byReplyId = getByReplyId(commentId);
        if (null == byReplyId) {
            throw new CheckRunException(CommonResultMessage.REPLAY_NOT_EXIST.message+commentId);
        }
        DiaryDO byDiaryId = userDiaryService.findById(byReplyId.getDiaryId());
        if (null == byDiaryId) {
            throw new CheckRunException(CommonResultMessage.DIARY_NOT_EXIST.message+byDiaryId.getId());
        }
        ForwardCommentVO forwardCommentVO = ForwardCommentVO.from(byReplyId, byDiaryId);
        UserInfoDTO userById = getUserById(byReplyId.getReplyUserId());
        UserInfoDTO userByDiaryId = getUserById(byDiaryId.getUserId());
        if (null == userById || null == userByDiaryId) {
            throw new CheckRunException(CommonResultMessage.USER_INVALID.message);
        }
        forwardCommentVO.setCommentUserName(userById.getName());
        forwardCommentVO.setCommentUserAvatarUrl(userById.getAvatarUrl());
        forwardCommentVO.setDiaryUserAvatarUrl(userByDiaryId.getAvatarUrl());
        forwardCommentVO.setDiaryUserName(userByDiaryId.getName());
        forwardCommentVO.setCommentTargetName(userById.getCurrentLearnTargetName());
        forwardCommentVO.setDiaryForwardCount(forwardService.countDiaryForward(byDiaryId.getId()));
        forwardCommentVO.setDiaryTargetName(userByDiaryId.getCurrentLearnTargetName());
        forwardCommentVO.setDiaryReplyCount(getCommentCountByDiaryId(byDiaryId.getId()));
        forwardCommentVO.setZanDiaryCount(userDiaryZanService.getZanCountByDiaryId(byDiaryId.getId(), UserDiaryZanDO.TypeEnums.DIARY.code));
        forwardCommentVO.setImgUrls(null);
        return forwardCommentVO;
    }

    /**
     * 日记列表之评论列表
     * @param diaryId
     * @param page
     * @param size
     * @param diaryUserId
     * @return
     */
    @Async("diaryTaskExecutor")
    public Future<List<UserDiaryListVO>> getDiaryListVO(Long diaryId, Integer page, Integer size, Long diaryUserId) {
        Specification<UserDiaryReplyDO> specification = (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicateList = Lists.newArrayList();
            predicateList.add(criteriaBuilder.equal(root.get("diaryId").as(Long.class), diaryId));
            return criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
        };
        Sort sort = new Sort(Sort.Direction.DESC, Lists.newArrayList("createTime"));
        PageRequest pageRequest = new PageRequest(page, size, sort).previous();
        org.springframework.data.domain.Page pageResult = userDiaryReplyRepository.findAll(specification, pageRequest);
        List<UserDiaryReplyDO> content = pageResult.getContent();
        if (null == content || content.size() == 0) {
            return null;
        }
        List<UserDiaryListVO> replyList = Lists.newArrayList();
        for (UserDiaryReplyDO diaryReply : content) {
            UserDiaryListVO userDiaryList = UserDiaryListVO.from(diaryReply);
            replyList.add(userDiaryList);
            UserInfoDTO replyUser = getUserById(diaryReply.getReplyUserId());
            userDiaryList.setReplyUserName(replyUser.getName());
            userDiaryList.setReplyUserAvatarUrl(replyUser.getAvatarUrl());
            userDiaryList.setUserCurrentLearnTargetName(replyUser.getCurrentLearnTargetName());
            UserInfoDTO replyedUser = getUserById(diaryReply.getReplyedUserId());
            userDiaryList.setReplyedUserName(replyedUser.getName());
            userDiaryList.setType(diaryReply.getType());
            userDiaryList.setAuthor(diaryUserId == userDiaryList.getReplyUserId().longValue());
        }
        return new AsyncResult<>(replyList);
    }

    public org.springframework.data.domain.Page findAll(Specification<UserDiaryReplyDO> specification, PageRequest pageRequest) {
        return userDiaryReplyRepository.findAll(specification, pageRequest);
    }
}
